/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package models;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import play.data.validation.Required;
import play.db.jpa.Model;

/**
 * 
 * @author Ousama
 */
@Entity
public class Topic extends Model {

	/****************************** RELATIONS! ********************************************/
	@ManyToOne
	public IdeaDeveloper creator;
	@ManyToMany(cascade = CascadeType.PERSIST)
	public List<Organizer> managers = new ArrayList<Organizer>();
	@ManyToMany(cascade = CascadeType.PERSIST)
	@JoinTable(name = "Topic1")
	public List<IdeaDeveloper> followed = new ArrayList<IdeaDeveloper>();
	@ManyToMany(cascade = CascadeType.PERSIST)
	@JoinTable(name = "Topic2")
	public List<IdeaDeveloper> joined = new ArrayList<IdeaDeveloper>();
	@ManyToMany(cascade = CascadeType.PERSIST)
	@JoinTable(name = "Topic3")
	public List<IdeaDeveloper> blocked = new ArrayList<IdeaDeveloper>();
	@ManyToOne
	public Organization topicOrg = null;
	@ManyToOne
	public Entities topicEnt = null;
	@OneToMany(mappedBy = "ideaTopic", cascade = CascadeType.ALL)
	public List<Idea> ideas = new ArrayList<Idea>();
	@ManyToMany
	public List<Tag> tags = new ArrayList<Tag>();
	@ManyToMany
	public List<ActionItem> actionItemTop = new ArrayList<ActionItem>();
	@OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
	public List<Rate> rates = new ArrayList<Rate>();
	@ManyToMany(cascade = CascadeType.PERSIST)
	@JoinTable(name = "Topi10")
	public List<IdeaDeveloper> unfollowed = new ArrayList<IdeaDeveloper>();
	@OneToMany(mappedBy="topic", cascade=CascadeType.PERSIST)
	public List<Request> requests = new ArrayList<Request>();
	/******************************* ATTRIBUTES! **********************************************/
	@Required
	public boolean hidden;
	@Required
	public String title;
	@Required
	public String description;
	public int numberOfViews = 0;
	@Required
	public Date dateOfDraftingPosting;
	public boolean published;
	public boolean closed;
	public boolean locked = false;
	public boolean promotedForExecution;

	/**
	 * Constructor for creating topic
	 * 
	 * @param title
	 *            title of the created topic
	 * @param description
	 *            the description of the topic
	 * @param IdeaDeveloperId
	 *            the idea developer creating topic
	 */
	public Topic(String title, String description, long IdeaDeveloperId) {
		IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper.find(
				"id=?", IdeaDeveloperId).first();
		this.title = title;
		this.description = description;
		this.creator = ideaDeveloper;
		this.closed = false;
		this.hidden = false;
		this.published = false;
		this.numberOfViews = 0;
		this.promotedForExecution = false;
		this.dateOfDraftingPosting = new Date();
		//ideaDeveloper.createdTopics.add(this);
		//ideaDeveloper.save();
		//this.followed.add(ideaDeveloper);
		//ideaDeveloper.followedTop.add(this);
		//ideaDeveloper.save();
		//Organizer o=(Organizer)Organizer.find("ideaDeveloper =?",ideaDeveloper).fetch().get(0);
		//this.managers.add(o);
		//o.managedTopics.add(this);
		//o.save();
		//this.save();
          
		Log log = new Log("^ is created.");
		log.ids = this.id + "";
		log.types = "topic";
		log.save();
	}

	/**
	 * Empty constructor for creating topics
	 */
	public Topic() {
	}

	/**
	 * creates a new topic in an organization
	 * 
	 * @author Amira
	 * 
	 * @param title
	 *            this is the title for the topic
	 * 
	 * @param description
	 *            this is the topic description
	 * 
	 * @param organizationId
	 *            id of the organization where the topic be created
	 * 
	 * @param ideaDeveloperId
	 *            id of the ideadeveloper who created this topic
	 * @return the created topic
	 */
	public static Topic createTopicOrg(String title, String description,
			long organizationId, long ideaDeveloperId) {
		Topic topic = new Topic(title, description, ideaDeveloperId);
		Organization org = (Organization) Organization
				.find("id=?", organizationId).fetch().get(0);
		IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper
				.find("id=?", ideaDeveloperId).fetch().get(0);
		topic.save();
		topic.topicOrg = org;
		topic.followed.add(ideaDeveloper);
		topic.save();
		ideaDeveloper.createdTopics.add(topic);
		ideaDeveloper.followedTop.add(topic);
		ideaDeveloper.save();
		org.topics.add(topic);
		org.save();

		Log log = new Log("^ created ^ in ^.");
		log.ids = ideaDeveloperId + "," + topic.id + "," + organizationId;
		log.types = "developer,topic,organization";
		log.save();

		return topic;
	}

	/**
	 * creates a new topic in an entity
	 * 
	 * @author Amira
	 * 
	 * @param title
	 *            this is the title for the topic
	 * 
	 * @param description
	 *            this is the topic description
	 * 
	 * @param entityId
	 *            id of the entity where the topic be created
	 * 
	 * @param ideaDeveloperId
	 *            id of the ideadeveloper who created this topic
	 * @return the created topic
	 */
	public static Topic createTopicEnt(String title, String description,
			long entityId, long ideaDeveloperId) {
		Topic topic = new Topic(title, description, ideaDeveloperId);
		Entities entity = (Entities) Entities.find("id=?", entityId).fetch()
				.get(0);
		IdeaDeveloper ideaDeveloper = (IdeaDeveloper) IdeaDeveloper
				.find("id=?", ideaDeveloperId).fetch().get(0);
		topic.save();
		topic.topicEnt = entity;
		topic.followed.add(ideaDeveloper);
		topic.save();
		ideaDeveloper.createdTopics.add(topic);
		ideaDeveloper.followedTop.add(topic);
		ideaDeveloper.save();
		entity.topics.add(topic);
		entity.save();

		Log log = new Log("^ created ^ in ^.");
		log.ids = ideaDeveloperId + "," + topic.id + "," + entityId;
		log.types = "developer,topic,entity";
		log.save();

		return topic;
	}

	/**
	 * helper delete method to delete the topic and all its components
	 * @author amira
	 */
	public void delete2() {
       while(!ideas.isEmpty()){
           ideas.get(0).delete2();
           }

           // deletes it from the actionItems
               for (int i = 0; i < actionItemTop.size(); i++) {
                       actionItemTop.get(i).topics.remove(this);
                       actionItemTop.get(i).save();
               }
               // deletes the tags relations
               for (int i = 0; i < tags.size(); i++) {
                       tags.get(i).topics.remove(this);
                       tags.get(i).save();
               }
               // deletes it from the creator created topics
               creator.createdTopics.remove(this);
               // deletes if from the IdeaDevelopers managedTopics
               for (int i = 0; i < managers.size(); i++) {
                       managers.get(i).managedTopics.remove(this);
                       managers.get(i).save();
               }
               // deleting being followed
               for (int i = 0; i < followed.size(); i++) {
                       followed.get(i).followedTop.remove(this);
                       followed.get(i).save();
               }
               for (int i = 0; i < unfollowed.size(); i++) {
                       unfollowed.get(i).unfollowedTop.remove(this);
                       unfollowed.get(i).save();
               }
               // deleting being joined
               for (int i = 0; i < joined.size(); i++) {
                       joined.get(i).joinedTop.remove(this);
                       joined.get(i).save();
               }

               // deleting the topics from the entities or from the org
               if (topicOrg != null) {
                       topicOrg.topics.remove(this);
                       topicOrg.save();
               } else {
                       topicEnt.topics.remove(this);
                       topicEnt.save();
               }

               while(!rates.isEmpty())
                       rates.get(0).delete2();



               Log log = new Log("^ is deleted.");
               log.ids = this.id + "";
               log.types = "topic";
               log.save();
               this.delete();
       }

	/**
	 * @author Dina
	 * 
	 *         non-static instance method that changes the visibility of the
	 *         topic to be hidden by setting the hidden attribute of the topic
	 *         invoked on to true
	 * 
	 */
	public void hideTopic() {
		this.hidden = true;
		this.save();

		Log log = new Log("^ is hidden.");
		log.ids = this.id + "";
		log.types = "topic";
		log.save();
	}

	/**
	 * 
	 * non-static instance method that changes the visibility of a hidden topic
	 * to be public by setting the hidden attribute of the topic invoked on to
	 * false
	 * 
	 * @author Dina
	 * 
	 */
	public void unhideTopic() {
		this.hidden = false;
		this.save();
		for (int i = 0; i < this.followed.size(); i++) {
			Notification.notify(" has chenged the visibily of topic "
					+ this.title + " to visible", this.followed.get(i),
					this.creator, new Date());
		}

		Log log = new Log("^ is un-hidden.");
		log.ids = this.id + "";
		log.types = "topic";
		log.save();
	}

	/**
	 * method to get all the idea developers in an organization that can be
	 * invited to a specific topic
	 * 
	 * @author: Dina
	 * 
	 * 
	 */
	public List<IdeaDeveloper> invitees() {
		if (this.topicOrg != null) {
			return this.topicOrg.members;
		}
		if (this.topicEnt != null) {
			return this.topicEnt.liesIn.members;
		}
		return null;
	}

	/**
	 * invites user to access a hidden topic
	 * 
	 * @author Dina
	 * 
	 * @param ideaDeveloperId
	 *            the invited idea developer
	 * 
	 */
	public void inviteToHidden(long ideaDeveloperId) {
		IdeaDeveloper ideaDev = IdeaDeveloper.findById(ideaDeveloperId);
		this.joined.add(ideaDev);
		this.save();
		ideaDev.joinedTop.add(this);
		ideaDev.save();
		IdeaDeveloper.sendEMail(ideaDev.eMail, "You were invited", "Dear "
				+ ideaDev.username + ": You were invited to the hidden topic "
				+ this.title + ". You can access it nw in anytime :)");
		/*
		 * Log log = new Log("^ is invited to ^."); log.ids = ideaDeveloperId +
		 * "," + this.id; log.types = "ideadeveloper,topic"; log.save();
		 */
	}

	/**
	 * posts a topic by changing published = true and putting the date
	 * 
	 * @author : Amira
	 * 
	 */
	public void postATopic() {
		this.published = true;
		this.dateOfDraftingPosting = new Date();
		this.save();

		Log log = new Log("^ is published.");
		log.ids = this.id + "";
		log.types = "topic";
		log.save();
	}

	/**
	 * creates topic in organization
	 * 
	 * @author Amira
	 * 
	 * @param dev
	 *            idea developer creating topic
	 * @param org
	 *            organization where topic lies
	 * @return the created topic
	 */
	public static Topic create(IdeaDeveloper dev, Organization org) {
		Topic topic = new Topic();
		topic.creator = dev;
		topic.topicOrg = org;
		topic.save();
		dev.createdTopics.add(topic);
		dev.save();
		org.topics.add(topic);
		org.save();

		Log log = new Log("^ added ^ to ^.");
		log.ids = dev.id + "," + topic.id + "," + org.id;
		log.types = "ideadeveloper,topic,organization";
		log.save();

		return topic;
	}

	/**
	 * creates topic in entity
	 * 
	 * @author Amira
	 * 
	 * @param dev
	 *            idea developer creating the topic
	 * @param ent
	 *            entity where topic lies
	 * @return the created topic
	 */
	public static Topic create(IdeaDeveloper dev, Entities ent) {
		Topic topic = new Topic();
		topic.creator = dev;
		topic.topicEnt = ent;
		topic.save();
		dev.createdTopics.add(topic);
		dev.save();
		ent.topics.add(topic);
		ent.save();

		Log log = new Log("^ added ^ to ^.");
		log.ids = dev.id + "," + topic.id + "," + ent.id;
		log.types = "developer,topic,entity";
		log.save();

		return topic;

	}

	/**
	 * takes idea developer and topic and checks if the user is blocked from the
	 * topic or blocked from the entity containing the topic or if he is already
	 * a follower in the topic
	 * 
	 * @author nataly.wadie
	 * @param follower
	 *            idea developer that wants to follow a topic
	 * @param topic
	 *            topic that is to be followed by the idea developer
	 * @return return true if he's already followed else return false
	 */
	public static boolean followCheck(IdeaDeveloper follower, Topic topic) {
		return topic.followed.contains(follower);
	}

	/**
	 * takes idea developer and topic and checks if he can follow this topic
	 * then he's added to the lists of following
	 * 
	 * @author nataly.wadie
	 * @param follower
	 *            idea developer that wants to follow a topic
	 * @param topic
	 *            topic that is to be followed by the idea developer
	 */
	public static void followTopic(IdeaDeveloper follower, Topic topic) {
		if (!followCheck(follower, topic)) {
			follower.followedTop.add(topic);
			follower.save();
			topic.followed.add(follower);
			topic.save();

			if (topic.unfollowed.contains(follower)) {
				topic.unfollowed.remove(follower);
				topic.save();
				follower.unfollowedTop.remove(topic);
				follower.save();
			}

			Log log = new Log("^ followed ^");
			log.ids = follower.id + "," + topic.id;
			log.types = "developer,topic";
			for (int i = 0; i < topic.managers.size(); i++) {
				IdeaDeveloper ideadeveloper = topic.managers.get(i).ideaDeveloper;
				Notification
						.notify(" has followed the topic <a href='/topicideacontroller/checkAuthorizationToViewTopic?topicId="
								+ topic.id + "'>" + topic.title + "</a>",
								ideadeveloper, follower, new Date());
			}
		}

		Log log = new Log("^ is following ^.");
		log.ids = follower.id + "," + topic.id;
		log.types = "developer,topic";
		log.save();
	}

	/**
	 * takes idea developer and topic and checks if the user is already a
	 * follower then remove him from the following lists
	 * 
	 * @author nataly.wadie
	 * @param unfollower
	 *            idea developer that doesn't want to be notified anymore from
	 *            this topic
	 * @param topic
	 *            topic that will be unfollowed by the idea developer
	 */
	public static void unfollowTopic(IdeaDeveloper unfollower, Topic topic) {
		if (followCheck(unfollower, topic)) {
			unfollower.followedTop.remove(topic);
			unfollower.save();
			topic.followed.remove(unfollower);
			topic.save();
			topic.unfollowed.add(unfollower);
			topic.save();
			unfollower.unfollowedTop.add(topic);
			unfollower.save();
			Log log = new Log("^ unfollowed ^");
			log.ids = unfollower.id + "," + topic.id;
			log.types = "developer,topic";
			for (int i = 0; i < topic.managers.size(); i++) {
				IdeaDeveloper ideadeveloper = topic.managers.get(i).ideaDeveloper;
				Notification
						.notify(" has unfollowed the topic <a href='/topicideacontroller/checkAuthorizationToViewTopic?topicId="
								+ topic.id + "'>" + topic.title + "</a>",
								ideadeveloper, unfollower, new Date());
			}
		}

		Log log = new Log("^ is no longer following ^.");
		log.ids = unfollower.id + "," + topic.id;
		log.types = "developer,topic";
		log.save();
	}

	/**
	 * closes a topic by changing closed = true
	 * 
	 * @author Sameha
	 */
	public void closeTopic() {
		this.closed = true;
		this.save();
		for (int i = 0; i < this.followed.size(); i++) {
			if (!followed.get(i).equals(this.creator)) {
				Notification.notify(" has closed topic " + this.title,
						this.followed.get(i), this.creator, new Date());
			}
		}
	}

	/**
	 * reOpens the topic by making closed = false
	 * 
	 * @author Sameha
	 */
	public void reOpenTopic() {
		this.closed = false;
		this.save();
		for (int i = 0; i < this.followed.size(); i++) {
			if (!followed.get(i).equals(this.creator)) {
				Notification.notify(" has re-opened topic " + this.title,
						this.followed.get(i), this.creator, new Date());
			}
		}
	}

	/**
	 * changes promotedForExecution to be true
	 * 
	 * @author Sameha
	 * 
	 */
	public void promoteTopic() {
		this.promotedForExecution = true;
		this.save();
		for (int i = 0; i < this.followed.size(); i++) {
			if (!followed.get(i).equals(this.creator)) {
				Notification.notify(" has promoted topic " + this.title
						+ " for execution", this.followed.get(i), this.creator,
						new Date());
			}
		}
	}

	/**
	 * Checks if the given tag is already a tag of this topic and invokes the
	 * method addTag() if it exits the while loop without returning true which
	 * means the tag does not exist & it is clear to go ahead and add the tag to
	 * this topic C3, story 5
	 * 
	 * @author Zein
	 * @param name
	 *            name of the tag to be added
	 * @return true if tag already exists
	 * @return false otherwise
	 */
	public boolean existingTag(String name) {
		int i = 0;
		while (i < this.tags.size()) {
			if (this.tags.get(i).name.equalsIgnoreCase(name)) {
				return true;
			}
			i++;
		}
		return false;
	}

	/**
	 * Relates the given tag to this Topic and it is invoked from the
	 * existingTag() method C3, story 5
	 * 
	 * @author Zein
	 * @param name
	 *            name of the tag to be added
	 */
	public void addTag(String name) {
		Tag thistag;
		List<Tag> tagIdea = Tag.find("name = ?", name).fetch();
		if (tagIdea.isEmpty()) {
			Tag newtag = new Tag(name);
			this.tags.add(newtag);
			newtag.addTopic(this);
			thistag = newtag;
		} else {
			this.tags.add(tagIdea.get(0));
			tagIdea.get(0).addTopic(this);
			thistag = tagIdea.get(0);
		}
		this.save();

		Log log = new Log("^ is added to ^.");
		log.ids = thistag.id + "," + this.id;
		log.types = "tag,topic";
		log.save();
	}

	/**
	 * Removes the tag from the topic's tags list and removes the topic from the
	 * tag's topics list.
	 * 
	 * @author Zein
	 * @param name
	 *            name of the tag to be removed
	 */
	public void removeTag(String name) {
		Tag thistag = null;
		int i = 0;
		while (i < this.tags.size()) {
			if (this.tags.get(i).name.equalsIgnoreCase(name)) {
				thistag = this.tags.get(i);
				this.tags.get(i).removeTopic(this);
				this.tags.remove(this.tags.get(i));
			}
			i++;
		}
		this.save();

		Log log = new Log("^ is removed from ^.");
		log.ids = thistag.id + "," + this.id;
		log.types = "tag,topic";
		log.save();
	}

	/**
	 * finds the organization where the topic exists
	 * 
	 * @return the organization
	 */
	public Organization orgWhereTopicExist() {
		Organization org;
		if (this.topicOrg != null) {
			org = this.topicOrg;
		} else {
			org = this.topicEnt.liesIn;
		}

		return org;
	}

	/**
	 * checks if a given ideadevoloper is a manager of a specifc topic
	 * 
	 * @author XtarX
	 * @param ideaDevoloperId
	 * @return true if the ideadevloper is a member of the topic , else returns
	 *         false
	 */
	// public static boolean isManager(Long ideaDevoloperId,long topicId) {
	//
	// // Topic topic = new Topic();
	// // IdeaDeveloper ideaDev = IdeaDeveloper.findById(ideaDevoloperId);
	// //
	// // for (int i = 0; i < topic.managers.size(); i++) {
	// //
	// // if (topic.managers.get(i).ideaDeveloper.username
	// // .equals(ideaDev.username)) {
	// //
	// // return true;
	// // }
	// // }
	// // return false;
	//
	// Topic topic=Topic.findById(topicId);
	// Organizer.find(query, params)
	// topic.managers.contains(ideaDevoloperId);
	//
	// }
	/**
	 * locks a specific topic thus cant be used to relate another topic
	 * 
	 * @author XtarX
	 * @param topicId the id of the topic that we want to use
	 * @return "done lock" when the topic is locked
	 */
	public static String lockTopic(Long topicId) {
		Topic topic = Topic.findById(topicId);
		topic.locked = true;
		topic.save();
		return "done LOCK";

	}

	/**
	 * unlocks a specific topic thus can be used to relate another topic
	 * 
	 * @author XtarX
	 * @param topicId the id of the topic that we want to use
	 * @return "done UNLOCK" when the topic is locked
	 */
	public static String unlockTopic(Long topicId) {
		Topic topic = Topic.findById(topicId);
		topic.locked = false;
		topic.save();
		return "done UNLOCK";

	}

	/**
	 * increment the view of topic every time the page is loaded
	 * 
	 * @author XtarX
	 * @param orgId
	 * @return the number of views incremented
	 */
	public  int incrementViews() {
		
		this.numberOfViews += 1;
		this.save();
		return this.numberOfViews;
	}

	public String toString() {

		return this.title;
	}
}
